#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <assert.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/dir.h>
#include <sys/stat.h>


#include "md5.h"
#include "bstrlib.h"
#include "server.h"
#include "fd_list.h"
#include "file_db.h"
#include "common.h"
#include "internal.h"


char *to_hexa(md5_byte_t *digest, size_t len) {
    unsigned int i = 0;
    char *result = NULL;

    result = calloc(len*2 + 1, sizeof(char));

    for (i = 0; result != NULL && i < len; ++i) {
        sprintf(result + i * 2, "%02x", digest[i]);
    }

    return result;
}

char *slice_md5(const char *a_path, unsigned int offset, unsigned int len) {
    char *res = NULL;
    md5_state_t state;
    md5_byte_t digest[16], content[BUFF];
    FILE *f = NULL;
    int f_res = 0;
    int i = 0, j = 0;

    f = fopen(a_path, "r");
    f_res = fseek(f, offset, SEEK_SET);
    md5_init(&state);

    i = len;
    do {
        j = MIN(i, BUFF);
        f_res = fread(&content, sizeof(md5_byte_t), j, f);
        md5_append(&state, (const md5_byte_t *)content, j);
        i -= j;
    } while (i > 0);

    md5_finish(&state, digest);
    fclose(f);

    res = to_hexa(digest, 16);

    return res;
}

int
load_files(files_db files_list){

    int count = 0,i = 0;
    struct dirent **files;

    /*Pedimos el listado de los archivos dentro de FOLDER*/
    count = scandir(FOLDER, &files, 0, alphasort);
    if (count < 0){
        perror("scandir");
        return -1;
    }
    else{
        /*Comenzamos el for en 2 para los directorios . y .. */
        for (i = 2; i < count; i++){
            load_file(files_list, files[i]->d_name);
            printf("Cargado archivo: %s\n", files[i]->d_name);
            free(files[i]);
        }
        /*Liberamos todo*/
        free(files[0]);
        free(files[1]);
        free(files);
    }
    return 1;
}

void
load_file(files_db files_list, char *name){
    md5_state_t state;
    md5_byte_t digest[16], content[BUFF];
    char *md5=NULL, *aux=NULL;
    int slices=0, size=0, div=0, i=0, j=0, read_res=0;
    file_db file;
    struct stat stat_res, *file_prop = NULL;
    FILE *f = NULL;
    part_db part;

    aux = calloc (strlen(FOLDER) + strlen(name)+2, sizeof(char));
    if (aux == NULL){
        perror("calloc-aux");
        return;
    }
    strcpy (aux,FOLDER);
    strncat (aux, name, strlen(name));
    f = fopen(aux, "r");
    fstat(fileno(f), &stat_res);
    i = stat_res.st_size;

    md5_init(&state);

    do {
        j = MIN(i,BUFF);;
        read_res = fread(&content, sizeof(md5_byte_t), j, f);
        md5_append(&state, (const md5_byte_t *)content, j);
        i -= j;
        /*md5_finish(&state, digest);
        md5 = to_hexa(digest, 16);
        free(md5);*/

    } while (i > 0);

    md5_finish(&state, digest);
    fclose(f);

    md5 = to_hexa(digest, 16);

    file_prop = calloc(1, sizeof(struct stat));
    if (file_prop == NULL){
        perror("calloc-file_prop");
        return;
    }
    if (stat (aux, file_prop) < 0){
        perror("stat");
    }
    else{
        size = (int)(file_prop->st_size);
    }

    div = size/1024;

    if (div*1024 == size){
        slices = div;
    }
    else{
        slices = div+1;
    }

    file = file_db_create(name, md5, slices, size);

    free(md5);
    md5=NULL;

    for (i=0;i<file_db_get_slices(file)-1;i++){
        md5 = slice_md5(aux, i, 1024);
        part = file_db_get_part(file, i+1);
        part_db_set_md5(part, md5);
        free(md5);
        md5=NULL;
    }

    files_add_file(files_list, file);

    free(aux);
    aux=NULL;
    free(file_prop);
    file_prop = NULL;
}
